/*===========================================================================*\
 * Fast Fourier Transform (Cooley-Tukey Method)
 *
 * (c) Vail Systems. Joshua Jung and Ben Bryan. 2015
 *
 * This code is not designed to be highly optimized but as an educational
 * tool to understand the Fast Fourier Transform.
\*===========================================================================*/
//------------------------------------------------
// Note: this code is not optimized and is
// primarily designed as an educational and testing
// tool.
// Proper optimization would involve memoization of
// the sin() and cos() calls... and to get high
// performace would require transforming the recursive
// calls into a loop and then loop unrolling. All of
// this is best accomplished in C or assembly.
//-------------------------------------------------

//-------------------------------------------------
// The following code assumes a complex number is
// an array: [real, imaginary]
//-------------------------------------------------
var complex = require('./complex');
var fftUtil = require('./fftutil');
var toWord = require( 'math-float32-to-word' )
var fs = require('fs');
//-------------------------------------------------
// Calculate FFT for vector where vector.length
// is assumed to be a power of 2.
//-------------------------------------------------

var fft_1 = function (vector,addr) {
    var X = [],
        N = addr.length;

    // Base case is X = x + 0i since our input is assumed to be real only.
    if (N == 1) {
      console.log('[[read]] ram',addr[0])
      return [[vector[addr[0]], 0]];
    }

    // Recurse: all even samples
    var X_evens = fft_1(vector,addr.filter(even)),

    // Recurse: all odd samples
        X_odds = fft_1(vector,addr.filter(odd));

    // Now, perform N/2 operations!
    console.log('-----------------------------')
    for (var k = 0; k < N / 2; k++)
    {
      /*
        var mm=fftUtil.exponent(k, N);
        console.log("++++++++++",toWord(mm[0]).toString(2));
        console.log("++++++++++",toWord(mm[1]).toString(2));
        */
        // t is a complex number!
        var t = X_evens[k],
            e = complex.multiply(fftUtil.exponent(k, N), X_odds[k]);
        console.log('>>read odd buffer',k)
        console.log('  complex multiply')
        console.log('>>read even buffer',k)

        console.log("read success fft_1",  X_evens[k], X_odds[k])        
        X[k] =         complex.add(t, e);
        X[k + (N/2)] = complex.subtract(t, e); 
        console.log('  complex add')
        console.log('  complex sub')
        console.log('<<write buffer',k)
        console.log('<<write buffer',k+(N/2))
        console.log("write success",  X_evens[k], X_odds[k])        
    }
    console.log('locate ram',X.length)
    console.log('close ram',X_evens.length,X_odds.length)

    function even(__, ix) {return ix % 2 == 0;}
    function odd (__, ix) {return ix % 2 == 1;}

    return X;
};
var x_end  = [0,0,0,0];
var bank_0 = new Array(256);
var bank_1 = new Array(256);
var bank_2 = new Array(256);
var bank_3 = new Array(256);
var buf = [bank_0, bank_1, bank_2, bank_3];
var bank_num = 4;
var exp_buf = [];
var FFT = 0b0001; 
var SET = 0b0010;
var GET = 0b0011;
var END = 0b1111;
var data_num = 0;
var fft = function (vector, addr, bank_idx, pos, level) {
    //var X = [],
    var N = addr.length;
    var instruction = 0;

    if (level == 0){
        fs.writeFileSync("fft.bin","", "utf8")
        instruction = SET << 12 | 0b00 <<10 | 0;        
        fs.appendFileSync("fft.bin", instruction.toString(16)+"\n", "utf8")

        fs.writeFileSync("exp.bin","", "utf8")

        data_num = N;
    }

    // Base case is X = x + 0i since our input is assumed to be real only.
    if (N == 1) {
      console.log('[[read]] ram',addr[0])
      end = x_end[bank_idx];
      console.log(bank_idx, end, addr[0])
      buf[bank_idx][end] = [vector[addr[0]],0];
      console.log("write bank " + bank_idx + ",  addr " + end,vector[addr[0]],0)
      x_end[bank_idx]++;
      console.log("==================================")
      //console.log(buf)
      return [bank_idx, end];
      //return [[vector[addr[0]], 0]];
    }
    var evens_idx = (bank_idx%2)*2;
    var odds_idx  = evens_idx + 1;
    
    // Recurse: all even samples
    var X_evens = fft(vector,addr.filter(even), evens_idx, "even", level+1),
    // Recurse: all odd samples
        X_odds = fft(vector,addr.filter(odd), odds_idx, "odd", level+1);

    // Now, perform N/2 operations!
    var even_idx  = X_evens[0];
    var even_addr = X_evens[1];
    var odd_idx  = X_odds[0];
    var odd_addr = X_odds[1];
    var start_idx = even_idx;
    var start_addr = even_addr;
    console.log(level, '-----------------------------')
    console.log("even", X_evens)
    console.log("odd", X_odds)


    var ram_even_addr;
    var ram_odd_addr;
    var addr_even_idx;
    var addr_odd_idx;

    if(odd_idx == 1){
        addr_odd_idx = 2;
    }else if(odd_idx == 2)
    {
        addr_odd_idx = 1;
    }else{
        addr_odd_idx = odd_idx;
    }
    
    if(even_idx == 1){
        addr_even_idx = 2;
    }else if(odd_idx == 2)
    {
        addr_even_idx = 1;
    }else{
        addr_even_idx = even_idx;
    }
    
    ram_even_addr = addr_even_idx * 256 + even_addr;
    ram_odd_addr  = addr_odd_idx * 256 + odd_addr;

    var gap = ram_odd_addr - ram_even_addr;
    console.log("the gap between even and odd",gap)
    
    var instruction = SET<<12 | 0b01<<10 | gap;
    fs.appendFileSync("fft.bin", instruction.toString(16)+"\n", "utf8")
    
    var cal_cell = 1;

    var round_times = 0;
    if( ((N/2)/cal_cell) >= 1)
    {
        round_times = ((N/2)/cal_cell);
    }else{
        round_times = 1;
    }


    for (var k = 0; k < round_times; k++)
    {

        /*
        var mm=fftUtil.exponent(k, N);
        console.log("++++++++++",toWord(mm[0]).toString(2));
        console.log("++++++++++",toWord(mm[1]).toString(2));
        */
        // t is a complex number!
        //var t = X_evens[k],
        //    e = complex.multiply(fftUtil.exponent(k, N), X_odds[k]);
        if(odd_idx == 1){
            addr_odd_idx = 2;
        }else if(odd_idx == 2)
        {
            addr_odd_idx = 1;
        }else{
            addr_odd_idx = odd_idx;
        }
        
        if(even_idx == 1){
            addr_even_idx = 2;
        }else if(odd_idx == 2)
        {
            addr_even_idx = 1;
        }else{
            addr_even_idx = even_idx;
        }
        
        ram_even_addr = addr_even_idx * 256 + even_addr;
        ram_odd_addr  = addr_odd_idx * 256 + odd_addr;

        var cell_use ;
        if (((N/2 - k*cal_cell)>= cal_cell))
        {
            cell_use = cal_cell;
        }else{
            cell_use = (N/2) - k*cal_cell;
        }

        instruction = (GET << 12) | (cell_use-1)<<10 | k * cal_cell;
        fs.appendFileSync("fft.bin", instruction.toString(16)+"\n", "utf8");
        instruction = (FFT << 12) | (cell_use-1)<<10 | ram_even_addr;
        fs.appendFileSync("fft.bin", instruction.toString(16)+"\n", "utf8");
        for (cell=0; cell<cell_use; cell++)
        {
            var pt_index = k * cal_cell + cell ;

            console.log('>>read odd buffer ',odd_idx, odd_addr)
            console.log('  complex multiply')
            console.log('>>read even buffer',even_idx, even_addr)
            var t = buf[even_idx][even_addr];
            var e = complex.multiply(fftUtil.exponent(pt_index, N), buf[odd_idx][odd_addr]);
            console.log("read success fft_0", buf[even_idx][even_addr], buf[odd_idx][odd_addr])        
            exp_value = fftUtil.exponent(pt_index, N)
            console.log("exp value", exp_value)
            exp_buf[data_num/N*pt_index] = exp_value;

            console.log('  complex add')
            console.log('  complex sub')
            console.log('<<write buffer',odd_idx, odd_addr)
            console.log('<<write buffer',even_idx, even_addr)
            buf[even_idx][even_addr] = complex.add(t, e);
            buf[odd_idx][odd_addr] = complex.subtract(t, e);

            console.log("write success",buf[even_idx][even_addr],buf[odd_idx][odd_addr])        
            
            if(even_idx==(bank_num-1)){
                even_addr = (even_addr + 1)%512;
            }
            even_idx = (even_idx+1) % bank_num;
            if(odd_idx==(bank_num-1)){
                odd_addr = (odd_addr + 1)%512;
            }
            odd_idx = (odd_idx+1) % bank_num;
        }

    }
    //console.log('locate ram',X.length)
    //console.log('close ram',X_evens.length,X_odds.length)

    function even(__, ix) {return ix % 2 == 0;}
    function odd (__, ix) {return ix % 2 == 1;}
    console.log(level, pos,"return start: ", start_idx, start_addr)
    console.log("==================================")
    //console.log(buf)

    instruction = (END) << 12 | 0;
    fs.appendFileSync("fft.bin", instruction.toString(16)+"\n", "utf8");

    if(level==0)
    {
        instruction = (END << 12) | 1 << 10 | 0;
        fs.appendFileSync("fft.bin", instruction.toString(16)+"\n", "utf8");
        for(i=0; i<data_num/2; i++)
        {
            console.log(exp_buf[i][0])
            fs.appendFileSync("exp.bin", toWord(exp_buf[i][0]).toString(16)+toWord(exp_buf[i][1]).toString(16)+"\n", "utf8" );
        }
    }

    return [start_idx, start_addr];
};

module.exports = {
    fft_cal : fft,
    fft_cor : fft_1,
    buffer  : buf 
}
